
#include <time.h>
#include <BOpenGL>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BSystem>

#include "member_BLineEdit.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BLineEdit*)_ptr)
#define member_allocate()		_ptr = new member_BLineEdit(this)
#define member_release()		delete (member_BLineEdit*)_ptr

BLineEdit::BLineEdit(BWidget* parent)
{
	member_allocate();
	this->setParent(parent);
}
BLineEdit::BLineEdit(const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setText(text);
}
BLineEdit::~BLineEdit()
{
	member_release();
}

bool BLineEdit::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
Align BLineEdit::align() const
{
	return member.align;
}

void BLineEdit::setSpacing(int spacing)
{
	if (member.spacing < 0)
		member.spacing = 0;
	if (member.spacing > 1000)
		member.spacing = 1000;
	if (member.spacing != spacing)
	{
		member.spacing = spacing;
		member.dirty = true;
		if (this->checks(Signal_SpacingChanged))
			this->emit(Signal_SpacingChanged, spacing);
		this->adjust();
		this->fresh();
	}
}
int BLineEdit::spacing() const
{
	return member.spacing;
}

bool BLineEdit::setText(const BString& text)
{
	if (member.block.text != text)
	{
		if (text.contain('\n'))
		{
			BString temp = text;
			temp.remove('\n');
			member.block.reset(temp);
		}
		else
		{
			member.block.reset(text);
		}
		member.dirty = true;
		emit(Signal_TextChanged, member.block.text);
		this->fresh();
		return true;
	}
	return false;
}
const BString& BLineEdit::text() const
{
	return member.block.text;
}

bool BLineEdit::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		if (readOnly)
			this->setCursor(Cursor_None);
		else
			this->setCursor(Cursor_Ibeam);
		member.dirty = true;
		if (member.readOnly)
			this->setCursor(Cursor_None);
		else
			this->setCursor(Cursor_Ibeam);
		this->emit(Signal_ReadOnly, readOnly);
		this->fresh();
		return true;
	}
	return false;
}
bool BLineEdit::readOnly() const
{
	return member.readOnly;
}

bool BLineEdit::setTextFormat(BTextFormat* textFormat)
{
	if (member.block.format != textFormat)
	{
		member.block.format = textFormat;
		if (textFormat)
		{
			textFormat->styled(this->realStyle());
			textFormat->changed(member.block.text);
		}
		member.block.dirty = true;
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
const BTextFormat* BLineEdit::textFormat() const
{
	return member.block.format;
}
BTextFormat* BLineEdit::textFormat()
{
	return member.block.format;
}

bool BLineEdit::setPrefix(const BString& prefix)
{
	if (member.prefix != prefix)
	{
		member.prefix = prefix;
		member.dirtySize = true;
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
const BString& BLineEdit::prefix() const
{
	return member.prefix;
}

bool BLineEdit::setPrefixAlign(Align prefixAlign)
{
	if (member.prefixAlign != prefixAlign)
	{
		member.prefixAlign = prefixAlign;
		if (member.prefix.size())
		{
			member.dirty = true;
			this->fresh();
		}
		return true;
	}
	return false;
}
Align BLineEdit::prefixAlign() const
{
	return member.prefixAlign;
}

bool BLineEdit::setSuffix(const BString& suffix)
{
	if (member.suffix != suffix)
	{
		member.suffix = suffix;
		member.dirtySize = true;
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
const BString& BLineEdit::suffix() const
{
	return member.suffix;
}

bool BLineEdit::setSuffixAlign(Align suffixAlign)
{
	if (member.suffixAlign != suffixAlign)
	{
		member.suffixAlign = suffixAlign;
		if (member.suffix.size())
		{
			member.dirty = true;
			this->fresh();
		}
		return true;
	}
	return false;
}
Align BLineEdit::suffixAlign() const
{
	return member.suffixAlign;
}

bool BLineEdit::query(const BString& text, BValue& value) const
{
	if (text == "text")
	{
		value = member.block.text;
		return true;
	}
	if (text == "text-size")
	{
		value = member.block.rect.size();
		return true;
	}
	if (text == "text-rect")
	{
		value = member.block.rect;
		return true;
	}
	if (text == "text-area")
	{
		value = member.textArea;
		return true;
	}
	if (text == "prefix-rect")
	{
		value = member.prefixRect;
		return true;
	}
	if (text == "suffix-rect")
	{
		value = member.suffixRect;
		return true;
	}
	if (text == "code-rects")
	{
		BPoint pos = member.block.rect.pos();
		BRectArray& codeRects = value.fill<BRectArray>();
		codeRects.reset(member.block.cells.size());
		for (int i = 0; i < member.block.cells.size(); i++)
		{
			codeRects[i] = member.block.cells[i].rect + pos;
		}
		return true;
	}
	return BWidget::query(text, value);
}

BSize BLineEdit::sizeHint() const
{
	if (const BFont* font = this->realFont())
	{
		int width = font->size() * 10;
		int height = font->size();
		if (member.dirtySize)
		{
			member.freshSizes();
			member.dirtySize = false;
		}
		if (member.prefixSize.valid())
		{
			width += member.prefixSize.width();
			height = bMax(height, member.prefixSize.height());
		}
		if (member.suffixSize.valid())
		{
			width += member.suffixSize.width();
			height = bMax(height, member.suffixSize.height());
		}
		return this->preferSize(width, height);
	}
	return BWidget::sizeHint();
}

void BLineEdit::updateEvent(const BEvent& event)
{
	if (this->focused() && !member.readOnly)
	{
		bool blinkShown = true;
		if (member.blinkTime > 0.0)
		{
			BReal now = event.now();
			BReal time = now - member.blinkTick;
			blinkShown = time <= (member.blinkTime / 2);
			if (time > member.blinkTime)
				member.blinkTick = now;
		}
		if (member.blinkShown != blinkShown)
		{
			member.blinkShown = blinkShown;
			this->fresh();
		}
	}
}
void BLineEdit::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BLineEdit::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	member.prefixStyle = 0;
	member.suffixStyle = 0;

	const BStyle* style = event.value();

	style_member(style)->assign(Value_Text, member.block.text);
	style_member(style)->assign(Value_Align, member.align);
	style_member(style)->assign(Value_Spacing, member.spacing);
	style_member(style)->assign(Value_Line_Width, member.lineWidth);

	if (const BStyle* prefixStyle = style->annex("prefix"))
	{
		member.prefixStyle = prefixStyle;
		style_member(prefixStyle)->assign(Value_Text, member.prefix);
		style_member(prefixStyle)->assign(Value_Align, member.prefixAlign);
	}
	if (const BStyle* suffixStyle = style->annex("suffix"))
	{
		member.suffixStyle = suffixStyle;
		style_member(suffixStyle)->assign(Value_Text, member.suffix);
		style_member(suffixStyle)->assign(Value_Align, member.suffixAlign);
	}

	if (BTextFormat* format = member.block.format)
	{
		format->styled(style);
	}

	member.block.font = this->realFont();
	member.block.dirty = true;
	member.dirtySize = true;
	member.dirty = true;
}
void BLineEdit::freshEvent(const BEvent& event)
{
	if (member.dirtySize)
	{
		member.freshSizes();
		member.dirtySize = false;
	}
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BLineEdit::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawForeground();

	State state = painter.state();
	if (this->focused() && member.block.index >= 0 && member.block.count)
	{
		const BRect& crect = this->centerRect();
		BPoint pos = member.block.rect.pos();
		painter.setColor(Color_Selection, state);
		int begin = bMin(member.block.index, member.block.index + member.block.count);
		int end = bMax(member.block.index, member.block.index + member.block.count);
		for (int i = begin; i < end; i++)
		{
			const ICodeCell& cell = member.block.cells[i];
			BRect cellRect = cell.rect + pos;
			painter.fillRect(cellRect);
		}
	}

	if (member.prefix.size())
	{
		painter.setStyle(member.prefixStyle);
		if (painter.setColor(Color_Text, state))
		{
			if (member.t_prefix.size())
				painter.drawText(member.prefixRect, member.t_prefix);
			else
				painter.drawText(member.prefixRect, member.prefix);
		}
	}
	if (member.suffix.size())
	{
		painter.setStyle(member.suffixStyle);
		if (painter.setColor(Color_Text, state))
		{
			if (member.t_suffix.size())
				painter.drawText(member.suffixRect, member.t_suffix);
			else
				painter.drawText(member.suffixRect, member.suffix);
		}
	}

	painter.setStyle(0);

	if (member.block.cells.size())
	{
		if (painter.setColor(Color_Text, state))
			member.block.paint(painter);
	}

	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	if (!member.readOnly && member.blinkShown && member.block.count == 0)
	{
		painter.setColor(Color_Indicator);
		BLine2 line = member.inputLine();
		painter.drawLine(line.begin(), line.end());
	}
}

void BLineEdit::focusEvent(const BEvent& event)
{
	if (this->focused())
	{
		member.focusedTime = event.time();
		member.block.index = 0;
		member.block.count = member.block.cells.size();
		event.accept(this->centerRect().leftBottom());
		this->fresh();
	}
	else
	{
		if (member.editing)
		{
			member.editing = false;
			emit(Signal_EditFinished, member.block.text);
		}
		member.blinkShown = false;
		this->fresh();
	}
	BWidget::focusEvent(event);
}
void BLineEdit::inputEvent(const BEvent& event)
{
	if (member.readOnly == false)
	{
		BCode code = event.value();
		bool edited = member.block.removeSelection();
		if (member.block.insert(code))
		{
			edited = true;
			member.blinkTick = event.now() + (BReal)0.5;
			BLine2 line = member.inputLine();
			event.accept(line.end());
		}
		if (edited)
		{
			member.emitEdited();
		}
	}
	BWidget::inputEvent(event);
}

void BLineEdit::keyPressEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	if (keyEvent.keyState(Key_Ctrl))
	{
		if (key == Key_C)
		{
			BString text;
			if (member.block.index >= 0)
			{
				if (member.block.count)
					member.block.collect(text);
				else
					text = member.block.text;
			}
			BSystem::SetClippedText(text);
			keyEvent.accept(true);
		}
		else if (key == Key_A)
		{
			member.block.index = 0;
			member.block.count = member.block.cells.size();
			member.blinkShown = false;
			this->fresh();
			keyEvent.accept(true);
		}
		else if (member.readOnly == false)
		{
			if (key == Key_V)
			{
				BString text;
				bool ret = BSystem::GetClippedText(text);
				text.replace('\n', ' ');
				member.block.removeSelection();
				member.block.insert(text);
				member.emitEdited();
				keyEvent.accept(true);
			}
			else if (key == Key_X)
			{
				BString text;
				if (member.block.index >= 0)
				{
					if (member.block.count)
						member.block.collect(text);
					else
						text = member.block.text;
				}
				BSystem::SetClippedText(text);
				if (member.block.removeSelection())
				{
					member.emitEdited();
				}
				keyEvent.accept(true);
			}
		}
	}
	else if (!member.readOnly)
	{
		if (key == Key_Enter)
		{
			keyEvent.accept(true);
			if (member.editing)
			{
				member.editing = false;
				emit(Signal_EditFinished, member.block.text);
			}
			member.blinkShown = false;
			this->fresh();
		}
		else if (key == Key_Home)
		{
			if (member.block.count)
			{
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index > 0)
			{
				member.block.index = 0;
				this->fresh();
			}
			member.blinkTick = keyEvent.now() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_End)
		{
			if (member.block.count)
			{
				member.block.index += member.block.count;
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index < member.block.cells.size())
			{
				member.block.index = member.block.cells.size();
				this->fresh();
			}
			member.blinkTick = keyEvent.now() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_Left)
		{
			if (member.block.count)
			{
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index > 0)
			{
				member.block.index--;
				this->fresh();
			}
			member.blinkTick = keyEvent.now() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_Right)
		{
			if (member.block.count)
			{
				member.block.index += member.block.count;
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index < member.block.cells.size())
			{
				member.block.index++;
				this->fresh();
			}
			member.blinkTick = keyEvent.now() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_Back)
		{
			if (member.block.count)
			{
				if (member.block.removeSelection())
				{
					member.emitEdited();
				}
			}
			else if (member.block.index > 0)
			{
				member.block.index--;
				if (member.block.remove(member.block.index))
				{
					member.emitEdited();
				}
			}
			member.blinkTick = keyEvent.now() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_Delete)
		{
			if (member.block.count)
			{
				if (member.block.removeSelection())
				{
					member.emitEdited();
				}
			}
			else
			{
				if (member.block.remove(member.block.index))
				{
					member.emitEdited();
				}
			}
			member.blinkTick = keyEvent.now() + (BReal)0.5;
			keyEvent.accept(true);
		}
	}
	BWidget::keyPressEvent(keyEvent);
}
void BLineEdit::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	member.pressed = false;
	member.pressedIndex = -1;
	this->fresh();
	BWidget::keyReleaseEvent(keyEvent);
}

void BLineEdit::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex)
	{
		member.hoverIndex = 0;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BLineEdit::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex)
	{
		member.hoverIndex = 0;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BLineEdit::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.time() - member.focusedTime < (BReal)0.3)
	{
		return;
	}
	if (mouseEvent.button() == Button_Left)
	{
		if (member.block.count || member.block.index != member.hoverIndex)
		{
			if (BSystem::KeyState(Key_Shift))
			{
				member.block.count = member.hoverIndex - member.block.index;
			}
			else
			{
				member.block.index = member.hoverIndex;
				member.block.count = 0;
			}
		}
		member.pressed = true;
		member.pressedIndex = member.hoverIndex;
		member.blinkTick = mouseEvent.now() + (BReal)0.5;
		this->fresh();
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BLineEdit::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.pressed)
		{
			member.pressed = false;
			this->fresh();
		}
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BLineEdit::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	pos.y() = this->height() / 2;
	int index = member.block.indexAt(pos);
	if (member.hoverIndex != index)
	{
		member.hoverIndex = index;
	}
	if (member.pressed)
	{
		int inputCount = member.pressedIndex - member.hoverIndex;
		if (member.block.index != member.hoverIndex || member.block.count != inputCount)
		{
			member.block.index = member.hoverIndex;
			member.block.count = inputCount;
			this->fresh();
		}
	}
	if (mouseEvent.button() == Button_None && this->focusable())
	{
		const BRect& crect = this->centerRect();
		if (crect.contain(pos))
			this->setCursor(Cursor_Ibeam);
		else
			this->setCursor(Cursor_None);
	}
	BWidget::mouseMoveEvent(mouseEvent);
}
void BLineEdit::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.block.cells.check(member.hoverIndex))
		{
			member.block.index = member.hoverIndex;
			member.block.count = 0;
			member.block.selectWord();
		}
		else
		{
			member.block.index = 0;
			member.block.count = member.block.cells.size();
		}
		this->fresh();
	}
	BWidget::mouseDoubleClickEvent(mouseEvent);
}

